package com.hdsx.netty.rpcserver.nio;

import com.hdsx.netty.protocol.BasicProtocol;

import java.io.*;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.*;

/**
 * @author 占贤银
 * time 2024/8/9 16:54
 * description 生命之灯因热情而点燃，生命之舟因拼搏而前行
 */
public class NioRegister {

    private int port;

    private List<String> classNames = new ArrayList<>();

    private Map<String, Object> registerMaps = new HashMap<>();

    public NioRegister(int port) {
        this.port = port;
    }

    public static void main(String[] args) {
        NioRegister register = new NioRegister(9999);
        register.publish();
    }

    public void publish() {
        doNioScan("com.hdsx.netty.rpcserver.nio.provider");

        doNioRegister();

        startUp();
    }

    private void doNioRegister() {
        try {
            for (String className : classNames) {
                Class<?> aClass = Class.forName(className);
                if (aClass.isInterface()) {
                    continue;
                }
                String interfaceName = aClass.getInterfaces()[0].getName();
                if (!registerMaps.containsKey(interfaceName)) {
                    registerMaps.put(interfaceName, aClass.newInstance());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void startUp() {
        try {
            Selector selector = Selector.open();
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            // todo 设置成非阻塞
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.bind(new InetSocketAddress(this.port));
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("NIO - RPC服务已启动，监听端口：" + this.port);
            while (true) {
                int selectId = selector.select();
                System.out.println("selectId = " + selectId);
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    handleProcess(selector, selectionKey);
                    iterator.remove();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void handleProcess(Selector selector, SelectionKey selectionKey) {
        try {
            if (selectionKey.isAcceptable()) {
                ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
                // 等待客户端连接
                SocketChannel socketChannel = serverSocketChannel.accept();
                // 设置非阻塞
                socketChannel.configureBlocking(false);
                socketChannel.register(selector, SelectionKey.OP_READ);
            } else if (selectionKey.isReadable()) {
                // todo 读事件 读取客户端发送过来的信息
                SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                int len = socketChannel.read(byteBuffer);
                ByteArrayInputStream byteArrayInputStream = null;
                ObjectInputStream ois = null;
                if (len > 0) {
                    byteBuffer.flip();
                    byteArrayInputStream = new ByteArrayInputStream(byteBuffer.array());
                    ois = new ObjectInputStream(byteArrayInputStream);
                    // todo 获取客户端传送过来的网络数据
                    BasicProtocol protocol = (BasicProtocol) ois.readObject();
                    System.out.println("protocol = " + protocol);
                    Object obj = registerMaps.get(protocol.getClassName());
                    Method method = obj.getClass().getMethod(protocol.getMethodName(), protocol.getParameterType());
                    Object result = method.invoke(obj, protocol.getParameterValues());
                    socketChannel.register(selector, SelectionKey.OP_WRITE);
                    selectionKey.attach(result);
                } else {
                    socketChannel.close();
                }
                ois.close();
                byteArrayInputStream.close();
                byteBuffer.clear();
            } else if (selectionKey.isWritable()) {
                SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                Object result = selectionKey.attachment();
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(bos);
                oos.writeObject(result);
                socketChannel.write(ByteBuffer.wrap("我们都是好人".getBytes()));
                oos.close();
                bos.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doNioScan(String packageName) {
        try {
            URL url = this.getClass().getClassLoader().getResource(packageName.replaceAll("\\.", "/"));
            File file = new File(url.getFile());
            for (File listFile : file.listFiles()) {
                String name = listFile.getName();
                if (listFile.isDirectory()) {
                    // todo 目录
                    doNioScan(packageName + "." + name);
                } else if (listFile.isFile()) {
                    // todo 文件
                    if (!name.endsWith(".class")) {
                        continue;
                    }
                    classNames.add((packageName+"." + name).replaceAll(".class", ""));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}